package io.lvdaxian.mybatis17.executor.keygen;

import io.lvdaxian.mybatis17.executor.Executor;
import io.lvdaxian.mybatis17.mapping.MappedStatement;
import io.lvdaxian.mybatis17.reflection.MetaObject;
import io.lvdaxian.mybatis17.session.Configuration;
import io.lvdaxian.mybatis17.session.RowBounds;

import java.sql.Statement;
import java.util.List;

/**
 * 键值生成器
 *
 * @author lihh
 */
public class SelectKeyGenerator implements KeyGenerator {
  public static final String SELECT_KEY_SUFFIX = "!selectKey";
  private boolean executeBefore;
  private MappedStatement keyStatement;
  
  public SelectKeyGenerator(MappedStatement keyStatement, boolean executeBefore) {
    this.keyStatement = keyStatement;
    this.executeBefore = executeBefore;
  }
  
  
  @Override
  public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    if (executeBefore) {
      processGeneratedKeys(executor, ms, parameter);
    }
  }
  
  @Override
  public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    if (!executeBefore) {
      processGeneratedKeys(executor, ms, parameter);
    }
  }
  
  /**
   * 这是 key 生成对象
   *
   * @param executor  生成器
   * @param ms        MappedStatement statement 语句
   * @param parameter 参数对象
   * @author lihh
   */
  private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {
    try {
      // 这是非空判断
      if (parameter != null && keyStatement != null && keyStatement.getKeyProperties() != null) {
        
        // 这里拿到属性
        String[] keyProperties = keyStatement.getKeyProperties();
        // 配置文件
        final Configuration configuration = ms.getConfiguration();
        // 根据参数 生成 参数元对象
        final MetaObject metaParam = configuration.newMetaObject(parameter);
        // 非空判断
        if (keyProperties != null) {
          // 通过事务 拿到执行器
          Executor keyExecutor = configuration.newExecutor(executor.getTransaction());
          // 查询数据 list
          List<Object> values = keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
          // 为空的处理
          if (values.isEmpty()) {
            throw new RuntimeException("SelectKey returned no data.");
            // 多条数据判断
          } else if (values.size() > 1) {
            throw new RuntimeException("SelectKey returned more than one value.");
          } else {
            // 对象结果
            MetaObject metaResult = configuration.newMetaObject(values.get(0));
            if (keyProperties.length == 1) {
              // 从这里设置 setter 方法
              if (metaResult.hasGetter(keyProperties[0])) {
                setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));
              } else {
                setValue(metaParam, keyProperties[0], values.get(0));
              }
            } else {
              handleMultipleProperties(keyProperties, metaParam, metaResult);
            }
          }
        }
      }
    } catch (Exception e) {
      throw new RuntimeException("Error selecting key or setting result to parameter object. Cause: " + e);
    }
  }
  
  /**
   * 这里 处理多个属性
   *
   * @param keyProperties 表示key 属性
   * @param metaParam     元参数
   * @param metaResult    元结果
   * @author lihh
   */
  private void handleMultipleProperties(String[] keyProperties, MetaObject metaParam, MetaObject metaResult) {
    // 拿到列的key
    String[] keyColumns = keyStatement.getKeyColumns();
    
    // 这是为空的场景
    if (keyColumns == null || keyColumns.length == 0) {
      // 这里遍历属性，依次设置值
      for (String keyProperty : keyProperties) {
        setValue(metaParam, keyProperty, metaResult.getValue(keyProperty));
      }
    } else {
      if (keyColumns.length != keyProperties.length) {
        throw new RuntimeException("If SelectKey has key columns, the number must match the number of key properties.");
      }
      for (int i = 0; i < keyProperties.length; i++) {
        setValue(metaParam, keyProperties[i], metaResult.getValue(keyColumns[i]));
      }
    }
  }
  
  /**
   * 表示 设置value值的 方法
   *
   * @param metaParam 表示对象元数据
   * @param property  对象属性值
   * @param value     对象具体的value值
   * @author lihh
   */
  private void setValue(MetaObject metaParam, String property, Object value) {
    // 判断是否有 setter 方法
    if (metaParam.hasSetter(property)) {
      // 具体设置的value
      metaParam.setValue(property, value);
    } else {
      throw new RuntimeException("No setter found for the keyProperty '" + property + "' in " + metaParam.getOriginalObject().getClass().getName() + ".");
    }
  }
}
